import React, { FC, ReactText, useState, useImperativeHandle, useEffect } from 'react';
import { Empty, Transfer, Tree } from 'antd';
import { TextWidget } from '@inbiz/react';
import classNames from 'classnames';
import { uniqBy, isString, isArray, cloneDeep, set as chainSet, get as chainGet } from 'lodash';
import { IFieldNames, IReqParams, ITree } from '../type';
import { apiWrapper, getListData, getTreeData } from '../../service';
import { useRequest } from 'ahooks';
import { InbizIcon, useEvents } from '@inbiz/utils';
import './style.less';
import { IRef, TransferProps } from '../..';
import api from '../../api';

const isChecked = (selectedKeys: ReactText[], eventKey: ReactText) =>
  selectedKeys.indexOf(eventKey) !== -1;

const treeFormatResult = (data: ITree[], isChild: boolean = false): ITree[] => {
  return (data || []).map((item) => ({
    ...item,
    isLeaf: (!isChild && isString(item.child)) || false, // - !item.child?.length, // 后端接口问题，默认都有展开按钮，如果数据之前已经判断了是叶子节点后不要在又展开按钮了
    child:
      isArray(item.child) && item.child?.length ? treeFormatResult(item.child, true) : undefined,
  }));
};
interface IProps extends Pick<TransferProps, 'callBackEvents' | 'emit' | 'onChange'> {
  className?: string;
  targetKeys: string[];
  fieldNames: IFieldNames;
  reqParams: IReqParams;
  disabled?: boolean;
  viewMode?:string;
  modelName: string;
  pRef: IRef;
}

const TreeTransfer: FC<IProps> = (props) => {
  const {
    className,
    targetKeys,
    reqParams,
    fieldNames,
    modelName,
    disabled,
    viewMode,
    onChange: onChangeProps,
    callBackEvents,
    emit,
    pRef,
    ...restProps
  } = props;
  const [configEvents] = useEvents(callBackEvents, emit);
  const [searchOption, setSearchOption] = useState<{
    expandedKeys: ReactText[];
    searchValue: string;
    autoExpandParent: boolean;
    loadedKeys: ReactText[];
  }>({
    expandedKeys: [],
    loadedKeys: [],
    searchValue: '',
    autoExpandParent: true,
  });
  const [treeData, setTreeData] = useState<ITree[]>([]);
  const [editSelectedData, setEditSelectedData] = useState<any[]>([]);
  const [load, setLoad] = useState(false);

  /**
   * 扩展代码相关包装
   */
  const getTreeDataService = apiWrapper(getTreeData, configEvents, {
    onRequest: 'onRequest',
    onParamFormat: 'onParamFormat',
    onData: 'onData',
  });

  useRequest(getTreeDataService, {
    defaultParams: [{ ...reqParams, parentId: reqParams?.parentId.split('$')[0], value: 0 }],
    onSuccess: (resData: any) => {
      const currentTreeData = treeFormatResult(resData?.data);
      setTreeData(currentTreeData);
      if (searchOption.expandedKeys.length === 0) {
        setSearchOption((prev) => ({
          ...prev,
          expandedKeys: currentTreeData.map((ele) => ele.id),
        }));
      }
      if (!load) {
        setLoad(true);
      }
      emit?.('onSuccess', resData);
    },
    onError: () => emit?.('onError'),
  });

  let transferDataSource = editSelectedData; // 穿梭框所用到的数据源
  /**
   * 将树形数据拉平添加到穿梭框的数据源中，主要用于穿梭框右侧列表，左侧使用的是树的数据
   * @param list 树
   */

  const flatten = (list: ITree[] = []) => {
    list.forEach((item) => {
      transferDataSource.push(item);
      flatten(item[fieldNames.children]);
    });
    transferDataSource = uniqBy(transferDataSource, fieldNames.key.split('$')[1]);
  };
  flatten(treeData);

  /**
   * 树本地搜索时展开收缩逻辑，参考 antd 的树搜索，穿梭框树因为是异步树，产品讨论去掉搜索
   * @param expandedKeys
   */
  const onExpand = (keys: ReactText[]) => {
    const { loadedKeys, expandedKeys } = searchOption;
    let nowLoadKeys = loadedKeys;
    // 已经展开的节点不应到再次去请求数据更新树（避免已加载的子节点丢失）
    expandedKeys.forEach((item: string | number) => {
      if (!nowLoadKeys.includes(item)) {
        nowLoadKeys.push(item);
      }
    });
    setSearchOption((prev) => ({
      ...prev,
      expandedKeys: keys,
      autoExpandParent: false,
      loadedKeys: nowLoadKeys,
    }));
  };

  /**
   * 树本地搜索时展开收缩逻辑，参考 antd 的树搜索，穿梭框树因为是异步树，产品讨论去掉搜索
   * @param key
   * @param tree
   * @returns
   */
  const getParentKey = (key: ReactText, tree: ITree[]): ReactText | undefined => {
    let parentKey: ReactText | undefined = undefined;
    for (let i = 0; i < tree.length; i++) {
      const node = tree[i];
      const children = node[fieldNames.children];
      if (children) {
        if (children.some((item: ITree) => item[fieldNames.key.split('$')[0]] === key)) {
          parentKey = node[fieldNames.key.split('$')[0]];
        } else if (getParentKey(key, children)) {
          parentKey = getParentKey(key, children);
        }
      }
    }
    return parentKey;
  };

  /**
   * 树本地搜索时展开收缩逻辑，参考 antd 的树搜索，穿梭框树因为是异步树，产品讨论去掉搜索
   * @param direction : ;
   * @param value
   */
  const onSearchChange = (direction: 'left' | 'right', value: string) => {
    if (direction === 'left') {
      const expandedKeys = transferDataSource
        .map((item) => {
          if (item[fieldNames.title.split('$')[1]].indexOf(value) > -1) {
            return getParentKey(item[fieldNames.key.split('$')[0]], treeData);
          }
          return null;
        })
        .filter((item, i, self) => item && self.indexOf(item) === i) as ReactText[];
      if (value) {
        setSearchOption({
          expandedKeys,
          loadedKeys: expandedKeys,
          searchValue: value,
          autoExpandParent: true,
        });
      } else {
        setSearchOption({
          expandedKeys: [],
          loadedKeys: [],
          searchValue: value,
          autoExpandParent: true,
        });
      }
    }
  };

  /**
   * 树本地搜索时展开收缩逻辑，参考 antd 的树搜索，穿梭框树因为是异步树，产品讨论去掉搜索
   * @param treeNodes
   * @param checkedKeys
   * @returns
   */
  const generateTree: (treeNodes: ITree[], checkedKeys: string[]) => ITree[] = (
    treeNodes = [],
    checkedKeys = [],
  ) => {
    const { searchValue } = searchOption;
    return treeNodes.map((props) => {
      const itemTitle = props[fieldNames.title.split('$')[1]] || '';
      const itemKey = props[fieldNames.key.split('$')[1]];
      const index = itemTitle.indexOf(searchValue);
      const beforeStr = itemTitle.substr(0, index);
      const afterStr = itemTitle.substr(index + searchValue.length);
      const title =
        index > -1 ? (
          <span>
            {beforeStr}
            <span style={{ color: '#f50' }}>{searchValue}</span>
            {afterStr}
          </span>
        ) : (
          <span>{itemTitle}</span>
        );
      return {
        [fieldNames.title.split('$')[1]]: title,
        [fieldNames.key.split('$')[1]]: itemKey + '',
        disabled: checkedKeys.includes(itemKey + ''),
        [fieldNames.children]: generateTree(props[fieldNames.children], checkedKeys),
        isLeaf: props.isLeaf,
      };
    });
  };

  /**
   * 对当前操作的 树节点挂在子树
   * @param list 树的原来数据
   * @param id 当前操作的节点id
   * @param child 挂载的新树形数据
   * @returns
   */
  const updateTreeData = (targetData: any, dataList: Array<ITree>): ITree[] => {
    const middle = cloneDeep(treeData);
    const indexList = targetData.pos.split('-').slice(1);
    const indexStr = indexList
      .map(
        (ele: string, index: number) => `[${ele}]${indexList.length > index + 1 ? '[child]' : ''}`,
      )
      .join('');
    const current = chainGet(middle, `${indexStr}`, {});
    chainSet(middle, `${indexStr}`, {
      ...current,
      isLeaf: dataList.length === 0,
      child:
        dataList.length === 0 ? undefined : dataList.map((ele: any) => ({ ...ele, isLeaf: false })),
    });
    return middle;
  };

  /**
   * 异步树加载数据，参考 antd 异步树加载
   * @param targetData
   * @returns
   */
  const loadData = async (targetData: ITree) => {
    try {
      const res = await getTreeDataService({
        ...reqParams,
        parentId: reqParams?.parentId.split('$')[0],
        value: targetData[fieldNames['key'].split('$')[1]],
      });
      const currentTreeData = updateTreeData(targetData, res.data ?? []);
      setTreeData(currentTreeData);
      // setTreeData(currentTreeData);
      if (searchOption.expandedKeys.length === 0) {
        setSearchOption((prev) => ({
          ...prev,
          expandedKeys: currentTreeData.map((ele) => ele.id),
        }));
      }
      emit?.('onSuccess', res);
      return Promise.resolve();
    } catch (e) {
      console.log(e);
      emit?.('onError');
      return Promise.resolve();
    }
  };

  const onChange = (v: string[]) => {
    onChangeProps?.(v);
    emit?.(
      'onSelect',
      v,
      transferDataSource.filter((item) => v.includes(item[fieldNames.key.split('$')[0]])),
    );
  };

  /**
   * 回显时通过 id 数组调用后端接口获取按照的节点数据，并保存到 state 中，后续用于展示已选框中的选中列表
   * @param ids 选中的 id 数组
   */
  const getSelectedList = async (ids: string[]) => {
    try {
      const totalKey = transferDataSource.map((item) => item[fieldNames.key.split('$')[0]]);
      if (targetKeys.length > 0 && targetKeys.some((key) => !totalKey.includes(key))) {
        const res = await getListData({
          appId: reqParams.appId,
          ids: ids,
          key: reqParams.sourceKey,
          field: fieldNames['key'],
          modelName,
        });
        setEditSelectedData(res);
      }
    } catch (e) {
      console.log(e);
    }
  };

  useImperativeHandle(
    pRef,
    () => ({
      ...api(configEvents, {
        ...props,
        setState: setTreeData,
        onChange,
        targetKeys,
        dataSource: transferDataSource,
        treeData,
        fieldNames,
      }),
    }),
    [transferDataSource, fieldNames, targetKeys],
  );

  useEffect(() => {
    if (load) {
      emit?.('onload');
    }
  }, [load]);

  useEffect(() => {
    getSelectedList(targetKeys);
  }, [targetKeys]);

  const titleRender = (nodeData: any) => {
    return (
      <span title={nodeData?.[fieldNames.title?.split('$')[1]]?.props?.children?.[2] || ''}>
        {nodeData?.[fieldNames.title?.split('$')[1]]}
      </span>
    );
  };

  return (
    <Transfer
      {...restProps}
      className={classNames(className, `${className}-tree`)}
      rowKey={(record) => record[fieldNames.key.split('$')[1]]}
      targetKeys={targetKeys}
      dataSource={transferDataSource}
      render={(item) => item[fieldNames.title.split('$')[1]]}
      onSearch={onSearchChange}
      showSelectAll={false}
      disabled={viewMode=='look'?true:false}
      onSelectChange={(e, v) => emit?.('onSelectChange', e, v)}
      onChange={onChange}
      showSearch={false}
      locale={{
        notFoundContent: (
          <Empty
            image={Empty.PRESENTED_IMAGE_SIMPLE}
            description={<TextWidget>Transfer.no_data</TextWidget>}
          />
        ),
      }}
    >
      {({ direction, onItemSelect, selectedKeys }) => {
        if (direction === 'left') {
          const checkedKeys = [...selectedKeys, ...targetKeys];
          if (!generateTree(treeData, targetKeys).length) {
            return (
              <Empty
                image={Empty.PRESENTED_IMAGE_SIMPLE}
                description={<TextWidget>Transfer.no_data</TextWidget>}
                style={{ top: 0, left: 0, right: 0, bottom: 0, margin: 'auto' }}
              />
            );
          } else {
            return (
              <Tree
                fieldNames={{
                  ...fieldNames,
                  title: fieldNames.title.split('$')[1],
                  key: fieldNames.key.split('$')[1],
                }}
                disabled={viewMode=='look'?true:disabled}
                blockNode
                checkable
                checkStrictly
                // defaultExpandAll
                // defaultExpandedKeys={searchOption.expandedKeys}
                checkedKeys={checkedKeys}
                treeData={generateTree(treeData, targetKeys)}
                onExpand={onExpand}
                switcherIcon={
                  <InbizIcon type="icon-tree-down" style={{ fontSize: 16, color: '#aaa' }} />
                }
                titleRender={titleRender}
                loadData={loadData}
                expandedKeys={searchOption.expandedKeys}
                loadedKeys={searchOption.loadedKeys}
                autoExpandParent={searchOption.autoExpandParent}
                onCheck={(_, { node: { key } }) => {
                  onItemSelect(key as string, !isChecked(checkedKeys, key));
                }}
                onSelect={(_, { node: { key } }) => {
                  onItemSelect(key as string, !isChecked(checkedKeys, key));
                }}
              />
            );
          }
        } else {
          return null;
        }
      }}
    </Transfer>
  );
};

export default TreeTransfer;
